switch (spcr->base_addr.space_id)
{
+ /* The address comes from the platform's own ACPI table, so it is trusted. */
case GRUB_ACPI_GENADDR_MEM_SPACE:
return grub_serial_ns8250_add_mmio (spcr->base_addr.addr,
- spcr->base_addr.access_size, &config);
+ spcr->base_addr.access_size, &config,
+ true);
case GRUB_ACPI_GENADDR_IO_SPACE:
- return grub_serial_ns8250_add_port (spcr->base_addr.addr, &config);
+ return grub_serial_ns8250_add_port (spcr->base_addr.addr, &config, true);
default:
return NULL;
};
}
struct grub_serial_port *
-grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config)
+grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config,
+ bool trusted)
{
struct grub_serial_port *p;
unsigned i;
return &com_ports[i];
}
+ /*
+ * use_mmio and port share a union with the private data of every other back
+ * end, so they only mean anything on a port this driver registered.
+ */
FOR_SERIAL_PORTS (p)
- if (p->use_mmio == false && p->port == port)
+ if (p->driver == &grub_ns8250_driver
+ && p->use_mmio == false && p->port == port)
{
if (config != NULL)
grub_serial_port_configure (p, config);
return p;
}
+ /* No port is registered here, so going on means probing the address. */
+ if (grub_serial_reject_untrusted_address (trusted) == true)
+ return NULL;
+
grub_outb (0x5a, port + UART_SR);
if (grub_inb (port + UART_SR) != 0x5a)
return NULL;
struct grub_serial_port *
grub_serial_ns8250_add_mmio (grub_addr_t addr, unsigned int acc_size,
- struct grub_serial_config *config)
+ struct grub_serial_config *config, bool trusted)
{
struct grub_serial_port *p;
unsigned i;
return &com_ports[i];
}
+ /*
+ * use_mmio and mmio share a union with the private data of every other back
+ * end, so they only mean anything on a port this driver registered.
+ */
FOR_SERIAL_PORTS (p)
- if (p->use_mmio == true && p->mmio.base == addr)
+ if (p->driver == &grub_ns8250_driver
+ && p->use_mmio == true && p->mmio.base == addr)
{
if (config != NULL)
grub_serial_port_configure (p, config);
return p;
}
+ /* No port is registered here, so going on means programming the address. */
+ if (grub_serial_reject_untrusted_address (trusted) == true)
+ return NULL;
+
p = grub_malloc (sizeof (*p));
if (p == NULL)
return NULL;
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/list.h>
+#include <grub/lockdown.h>
#ifdef GRUB_MACHINE_MIPS_LOONGSON
#include <grub/machine/kernel.h>
#endif
\f
+/*
+ * An address that no port is registered at cannot be honored while lockdown
+ * is enforced: the probe and programming sequences in the ns8250 and pl011
+ * back ends write to fixed offsets from it with no way to tell a UART from
+ * ordinary memory, so an address taken from an unauthenticated configuration
+ * is a write primitive against whatever it points at.
+ *
+ * A back end calls this once its own lookup has found no port already
+ * registered at the address, that is, at the point where it would otherwise
+ * touch the hardware. TRUSTED is true when the address came from a platform
+ * description -- grub_ns8250_init(), an ACPI SPCR table, PCI enumeration --
+ * rather than from the caller.
+ */
+bool
+grub_serial_reject_untrusted_address (bool trusted)
+{
+ if (trusted == true || grub_is_lockdown () != GRUB_LOCKDOWN_ENABLED)
+ return false;
+
+ grub_error (GRUB_ERR_ACCESS_DENIED,
+ N_("serial port address is not permitted when lockdown is enforced"));
+ return true;
+}
+
struct grub_serial_port *
grub_serial_find (const char *name)
{
if (grub_strcmp (port->name, name) == 0)
return port;
+ /*
+ * Everything below this point builds a port from an address the caller
+ * gave us, so the back ends are told the address is untrusted: under
+ * lockdown they hand back a port only if one is already registered at that
+ * address, which means the platform described that UART.
+ */
#if (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) && !defined(GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC)
if (grub_strncmp (name, "port", sizeof ("port") - 1) == 0
&& grub_isxdigit (name [sizeof ("port") - 1]))
{
port = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1],
- 0, 16), NULL);
+ 0, 16), NULL, false);
if (port != NULL)
return port;
}
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("incorrect MMIO access size"));
}
- port = grub_serial_ns8250_add_mmio (addr, acc_size, NULL);
+ port = grub_serial_ns8250_add_mmio (addr, acc_size, NULL, false);
if (port != NULL)
return port;
}
port = grub_serial_find (name);
if (!port)
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- N_("serial port `%s' isn't found"),
- name);
+ {
+ /*
+ * A back end that refused the port has already said why; keep that
+ * rather than reporting a misleading "isn't found".
+ */
+ if (grub_errno == GRUB_ERR_ACCESS_DENIED)
+ return grub_errno;
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("serial port `%s' isn't found"),
+ name);
+ }
config = port->config;
return port->driver->configure (port, &config);
}
+bool grub_serial_reject_untrusted_address (bool trusted);
+
#if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
void grub_ns8250_init (void);
struct grub_serial_port *grub_ns8250_spcr_init (void);
struct grub_serial_port *grub_serial_ns8250_add_port (grub_port_t port,
- struct grub_serial_config *config);
+ struct grub_serial_config *config,
+ bool trusted);
struct grub_serial_port *grub_serial_ns8250_add_mmio (grub_addr_t addr,
unsigned int acc_size,
- struct grub_serial_config *config);
+ struct grub_serial_config *config,
+ bool trusted);
#endif
#ifdef GRUB_MACHINE_IEEE1275
void grub_ofserial_init (void);